/*
 *	Copyright (C) 2008 CRIMERE
 *	Copyright (C) 2008 Jean-Marc Mercier
 *	
 *	This file is part of OTS (optimally transported schemes), an open-source library
 *	dedicated to scientific computing. http://code.google.com/p/optimally-transported-schemes/
 *
 *	CRIMERE makes no representations about the suitability of this
 *	software for any purpose. It is provided "as is" without express or
 *	implied warranty.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
#if !defined _performance_analyzer_
#define _performance_analyzer_

#include <map>
#include <set>
#include <string>
#include <time.h>
#include <src/miscellaneous/OS_basic.h>
#include <iostream>

template<class Cell >
struct Call_Graph {
	typedef Cell									cell_type;
	typedef typename Cell::cell_parameter_type		cell_parameter_type;
	typedef smart_ptr<Call_Graph>					smart_ptr_type;
	typedef std::map<std::string,smart_ptr_type>	map_type;

	Call_Graph(){};
	Call_Graph(smart_ptr_type Parent) : Parent_(Parent){
//		Parent_.reset();
	};
	virtual ~Call_Graph(){};
	void operator()(const cell_parameter_type & arg) {
		Cell_(arg);
	};
	void clear();

	smart_ptr_type			Parent_;
	map_type				Childs_;
	cell_type				Cell_;
};

template<class Cell>
std::ostream &operator<<(std::ostream &out, const Call_Graph<Cell> &v) {
		typedef Call_Graph<Cell> graph_type;
		if (v.Childs_.size() > 0 || v.Parent_ == 0) {
			out << std::endl << "----------------------------" << std::endl;
			out << v.Cell_;
			out << std::endl << "----------------------------" << std::endl;
		};
		graph_type::map_type::const_iterator it = v.Childs_.begin(), it_end = v.Childs_.end();
		if (v.Parent_ != 0) {
			for (;it != it_end;++it) {
				OS_double percent = 100.;
				if (v.Cell_.time_ > 0)
					percent = 100.*it->second->Cell_.time_ / v.Cell_.time_;
				out << std::endl << " stat for " << v.Cell_.name_ << "--" << it->second->Cell_ << "  (" <<  percent << " % )";
			}
		}
		it = v.Childs_.begin();
		for (;it != it_end;++it)
				out << *(it->second);

	  return out;
};



template <class Graph>
class Analyzer {
public :
	typedef Graph								graph_type;	
	typedef typename Graph::smart_ptr_type		smart_ptr_type;
	typedef  Singleton<Analyzer >				Analyzer_Mgr;
	Analyzer() : current_call_(new graph_type), first_call_(current_call_) {
	};
	virtual ~Analyzer(){};
	smart_ptr_type operator()(const std::string & name){
			smart_ptr_type ptr = (*current_call_).Childs_[name];
			if (ptr == 0) {
				ptr = smart_ptr_type(new graph_type(current_call_));
				ptr->Cell_.name_ = name;
				(*current_call_).Childs_[name] = ptr;
			}
			current_call_= ptr;
			return current_call_;
	};
	void clear(){
/*		smart_ptr_type Genitor(current_call_);
		for (;Genitor->Parent_ != 0; Genitor = Genitor->Parent_){};
		Genitor->clear();*/
		current_call_ = smart_ptr_type(new graph_type);
		first_call_ = current_call_;
	};
	void up() {
		current_call_ = current_call_->Parent_;
		if (current_call_ == first_call_)
			std::cout << *this;
	};
	
	smart_ptr_type current_call_;
	smart_ptr_type first_call_;
};


	template <class G>
	std::ostream &operator<<(std::ostream &out, const Analyzer<G> &v) {
		typedef Analyzer<G>::smart_ptr_type smart_ptr_type;
		smart_ptr_type Genitor(v.current_call_);
		for (;Genitor->Parent_ != 0; Genitor = Genitor->Parent_){};
		out << *Genitor << std::endl;
	  return out;
	};


	struct Time_Analyzer_Cell {
		typedef OS_double cell_parameter_type;
		Time_Analyzer_Cell();
		Time_Analyzer_Cell(const std::string & name);
		virtual ~Time_Analyzer_Cell(){};
		void operator()(OS_double time);
		OS_double time_;
		OS_int count_;
		std::string name_;
	};

	std::ostream &operator<<(std::ostream &out, const Time_Analyzer_Cell &v);
	class Analyzer_Timer {
	public :
		typedef  Singleton<Analyzer<Call_Graph<Time_Analyzer_Cell> > > Analyzer_Mgr;

		Analyzer_Timer(const std::string & name);
		virtual ~Analyzer_Timer();
		static void clear();
		Call_Graph<Time_Analyzer_Cell>::smart_ptr_type smart_ptr_;
		OS_timer timer_;
	};

#endif